package intelligentcity.model.traffic;

import intelligentcity.model.City;
import intelligentcity.model.Move;
import intelligentcity.model.Point;
import intelligentcity.model.Road;
import intelligentcity.model.intersection.Intersection;
import intelligentcity.model.streets.OneWayStreet;
import intelligentcity.model.streets.Street;

import java.util.ArrayList;
import java.util.List;
import java.util.Random;

public class Car extends Thread {

    private static Random random = new Random();
    private final static int distanceBetweenCars = 20;
    private final static int timeOfsleeping = 5;

    private final City city;
    private final Point point;
    private final Traffic traffic;
    private final int id;

    private Road currentRoad;
    private OneWayStreet currentOWS;

    public Car(City city, Traffic traffic, int id) {

        this.city = city;
        this.traffic = traffic;
        this.id = id;
        point = city.getRandomStartPoint();

        OneWayStreet oneWaystreet = null;
        for (Street street : city.getStreets()) {
            oneWaystreet = getOWSofCurrentPoint(street);
            if (oneWaystreet != null) {
                currentOWS = oneWaystreet;
                currentRoad = street;
                break;
            }
        }
    }
    
    @Override
    public void run() {
        while (point.getX() >= city.getLeftborderoftraffic() && point.getY() >= city.getUpperborderoftraffic()
                && point.getX() <= city.getRightborderoftraffic() && point.getY() <= city.getLowerborderoftraffic()) {
            go();
        }
        traffic.getCars().remove(id);
        System.out.println("Removed id: " + id);
    }

    // TODO
    private void go() {

        try {
            sleep(timeOfsleeping);
        } catch (InterruptedException e) {
            e.printStackTrace();
        }

        if (!canIMove(currentOWS.getMove())) {
            return;
        }

        if (currentRoad instanceof Intersection) {
            for (Point pointOfTurn : ((Intersection) currentRoad).getPointsOfTurn()) {
                if (point.equals(pointOfTurn)) {
                    changeStreet();
                }
            }
        }

        if (currentOWS.getMove() == Move.UP) {
            if (currentOWS.contains(point.getX(), point.getY() - 1)) {
                point.decY();
            } else {
                changeRoad();
            }

        }
        if (currentOWS.getMove() == Move.DOWN) {
            if (currentOWS.contains(point.getX(), point.getY() + 1)) {
                point.incY();
            } else {
                changeRoad();
            }
        }
        if (currentOWS.getMove() == Move.LEFT) {
            if (currentOWS.contains(point.getX() - 1, point.getY())) {
                point.decX();
            } else {
                changeRoad();
            }
        }
        if (currentOWS.getMove() == Move.RIGHT) {
            if (currentOWS.contains(point.getX() + 1, point.getY())) {
                point.incX();
            } else {
                changeRoad();
            }
        }

    }



    private boolean toClose(int front, int back) {
        if (front - back < distanceBetweenCars && front - back > 0) {
            return true;
        }
        return false;
    }

    public Point getPoint() {
        return point;
    }

    public City getCity() {
        return city;
    }

    public OneWayStreet getCurrentOWS() {
        return currentOWS;
    }

    private OneWayStreet getOWSofCurrentPoint(Road r) {
        OneWayStreet oneWayStreet;
        if (r instanceof Street) {
            oneWayStreet = ((Street) r).getFirstStreet();
            if (oneWayStreet.contains(point)) {
                return oneWayStreet;
            }
            oneWayStreet = ((Street) r).getSecondStreet();
            if (oneWayStreet.contains(point)) {
                return oneWayStreet;
            }
        }
        if (r instanceof Intersection) {
            oneWayStreet = ((Intersection) r).getHorizontalStreet().getFirstStreet();
            if (oneWayStreet.contains(point)) {
                return oneWayStreet;
            }
            oneWayStreet = ((Intersection) r).getHorizontalStreet().getSecondStreet();
            if (oneWayStreet.contains(point)) {
                return oneWayStreet;
            }
            oneWayStreet = ((Intersection) r).getVerticalStreet().getFirstStreet();
            if (oneWayStreet.contains(point)) {
                return oneWayStreet;
            }
            oneWayStreet = ((Intersection) r).getVerticalStreet().getSecondStreet();
            if (oneWayStreet.contains(point)) {
                return oneWayStreet;
            }
        }
        return null;
    }

    private OneWayStreet getRandomOWSofCurrentPoint(Road r) {
        List<OneWayStreet> list = new ArrayList<>();
        OneWayStreet oneWayStreet;
        if (r instanceof Street) {
            oneWayStreet = ((Street) r).getFirstStreet();
            if (oneWayStreet.contains(point)) {
                list.add(oneWayStreet);
            }
            oneWayStreet = ((Street) r).getSecondStreet();
            if (oneWayStreet.contains(point)) {
                list.add(oneWayStreet);
            }
        }
        if (r instanceof Intersection) {
            oneWayStreet = ((Intersection) r).getHorizontalStreet().getFirstStreet();
            if (oneWayStreet.contains(point)) {
                list.add(oneWayStreet);
            }
            oneWayStreet = ((Intersection) r).getHorizontalStreet().getSecondStreet();
            if (oneWayStreet.contains(point)) {
                list.add(oneWayStreet);
            }
            oneWayStreet = ((Intersection) r).getVerticalStreet().getFirstStreet();
            if (oneWayStreet.contains(point)) {
                list.add(oneWayStreet);
            }
            oneWayStreet = ((Intersection) r).getVerticalStreet().getSecondStreet();
            if (oneWayStreet.contains(point)) {
                list.add(oneWayStreet);
            }
        }
        return list.get(random.nextInt(list.size()));
    }

    private void changeStreet() {
        currentOWS = getRandomOWSofCurrentPoint(currentRoad);

        try {
            sleep(30);
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
    }

    private void changeRoad() {

        List<Road> roads = new ArrayList<>();
        OneWayStreet oneWaystreet = null;
        for (Road r : currentRoad.getNeighbors()) {
            oneWaystreet = getOWSofCurrentPoint(r);
            if (oneWaystreet != null && oneWaystreet != currentOWS) {
                roads.add(r);
            }
        }
        if (roads.size() > 0) {
            currentRoad = roads.get(random.nextInt(roads.size())); // change of Road
            currentOWS = getOWSofCurrentPoint(currentRoad); // without move
        }

        /*
         * try { sleep(1000); } catch (InterruptedException e) { e.printStackTrace(); }
         */
    }

    private boolean canIMove(Move move) {
        // pierwszy arg toCase powinien miec wieksza wartosc
        switch (move) {
            case UP: {
                for (Integer i : traffic.getCars().keySet()) {
                    if (point.getX() == traffic.getCars().get(i).getPoint().getX()) {
                        if (toClose(point.getY(), traffic.getCars().get(i).getPoint().getY())) {
                            return false;
                        }
                    }

                }
                break;
            }
            case DOWN: {
                for (Integer i : traffic.getCars().keySet()) {
                    if (point.getX() == traffic.getCars().get(i).getPoint().getX()) {
                        if (toClose(traffic.getCars().get(i).getPoint().getY(), point.getY())) {
                            return false;
                        }
                    }
                }
                break;
            }
            case LEFT: {
                for (Integer i : traffic.getCars().keySet()) {
                    if (point.getY() == traffic.getCars().get(i).getPoint().getY()) {
                        if (toClose(point.getX(), traffic.getCars().get(i).getPoint().getX())) {
                            return false;
                        }
                    }
                }
                break;
            }
            case RIGHT: {
                for (Integer i : traffic.getCars().keySet()) {
                    if (point.getY() == traffic.getCars().get(i).getPoint().getY()) {
                        if (toClose(traffic.getCars().get(i).getPoint().getX(), point.getX())) {
                            return false;
                        }
                    }
                }
                break;
            }
        }
        return true;
    }


}
